/*
 * Copyright (C) 2013 gujicheng
 * 
 * Licensed under the GPL License Version 2.0;
 * you may not use this file except in compliance with the License.
 * 
 * If you have any question, please contact me.
 * 
 *************************************************************************
 **                   Author information                                **
 *************************************************************************
 ** Email: gujicheng197@126.com                                         **
 ** QQ   : 29600731                                                     **
 ** Weibo: http://weibo.com/gujicheng197                                **
 *************************************************************************
 */
package com.libra.sinvoice

import android.os.Handler
import android.os.Message
import android.text.TextUtils

import com.libra.sinvoice.Buffer.BufferData
import java.lang.ref.WeakReference

class SinVoiceRecognition @JvmOverloads constructor(codeBook: String = Common.CODE_BOOK, sampleRate: Int = Common.DEFAULT_SAMPLE_RATE, bufferSize: Int = Common.DEFAULT_BUFFER_SIZE, bufferCount: Int = Common.DEFAULT_BUFFER_COUNT) : Record.Listener, Record.Callback, VoiceRecognition.Listener, VoiceRecognition.Callback {

    private val mBuffer: Buffer
    private val mRecord: Record
    private val mRecognition: VoiceRecognition

    private var mRecordThread: Thread? = null
    private var mRecognitionThread: Thread? = null
    private var mState: Int = 0
    private var mListener: Listener? = null

    private var mCodeBook: String? = null
    private val mMaxCodeIndex: Int
    private var resultBuffer = StringBuffer()
    private var mHanlder: Handler? = null

    override val recognitionBuffer: BufferData?
        get() {
            val buffer = mBuffer.full
            if (null == buffer) {
                LogHelper.d(TAG, "get null full buffer")
            }
            return buffer
        }

    interface Listener {
        fun onRecognitionStart()
        //        fun onRecognition(ch: Char)
        fun onRecognition(result: String)

        fun onRecognitionEnd()
    }

    init {
        mState = STATE_STOP
        mBuffer = Buffer(bufferCount, bufferSize)

        mRecord = Record(this, sampleRate, Record.CHANNEL_1, Record.BITS_16, bufferSize)
        mRecord.setListener(this)
        mRecognition = VoiceRecognition(this, sampleRate, Record.CHANNEL_1, Record.BITS_16)
        mRecognition.setListener(this)

        mMaxCodeIndex = Encoder.maxCodeCount - 2
        mHanlder = RegHandler(this)
        setCodeBook(codeBook)
        Common.init()
    }

    fun setListener(listener: Listener) {
        mListener = listener
    }

    fun getListener(): Listener? {
        return mListener
    }

    fun setCodeBook(codeBook: String) {
        if (!TextUtils.isEmpty(codeBook) && codeBook.length <= mMaxCodeIndex) {
            mCodeBook = codeBook
        }
    }

    fun start() {
        if (STATE_STOP == mState) {
            mState = STATE_PENDING

            mRecognitionThread = object : Thread() {
                override fun run() {
                    mRecognition.start()
                }
            }
            if (null != mRecognitionThread) {
                mRecognitionThread!!.start()
            }

            mRecordThread = object : Thread() {
                override fun run() {
                    mRecord.start()
                    stopRecognition()
                }
            }
            if (null != mRecordThread) {
                mRecordThread!!.start()
            }

            mState = STATE_START
        }
    }

    private fun stopRecognition() {
        mRecognition.stop()

        // put end buffer
        val data = BufferData(0)
        mBuffer.putFull(data)

        if (null != mRecognitionThread) {
            try {
                mRecognitionThread!!.join()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            } finally {
                mRecognitionThread = null
            }
        }

        mBuffer.reset()
    }

    fun stop() {
        if (STATE_START == mState) {
            mState = STATE_PENDING

            mRecord.stop()
            if (null != mRecordThread) {
                try {
                    mRecordThread!!.join()
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                } finally {
                    mRecordThread = null
                }
            }

            mState = STATE_STOP
        }
    }

    override fun onStartRecord() {
        LogHelper.d(TAG, "start record")
    }

    override fun onStopRecord() {
        LogHelper.d(TAG, "stop record")
    }

    override fun getRecordBuffer(): BufferData? {
        val buffer = mBuffer.empty
        if (null == buffer) {
            LogHelper.d(TAG, "get null empty buffer")
        }
        return buffer
    }

    override fun freeRecordBuffer(buffer: BufferData?) {
        if (null != buffer) {
            if (!mBuffer.putFull(buffer)) {
                LogHelper.d(TAG, "put full buffer failed")
            }
        }
    }

    override fun freeRecognitionBuffer(buffer: BufferData) {
        if (!mBuffer.putEmpty(buffer)) {
            LogHelper.d(TAG, "put empty buffer failed")
        }
    }

    override fun onStartRecognition() {
        LogHelper.d(TAG, "start recognition")
    }

    override fun onRecognition(index: Int) {
        LogHelper.d(TAG, "zzzzzzzzzzzzzrecognition:$index")
        if (Common.START_TOKEN == index) {
            resultBuffer.setLength(0)
            mHanlder!!.sendEmptyMessage(MSG_RECG_START)
        } else if (Common.STOP_TOKEN == index) {
            mHanlder!!.sendEmptyMessage(MSG_RECG_END)
        } else if (index > 0 && index <= mMaxCodeIndex) {
//            mListener?.onRecognition(mCodeBook!![index - 1])
            resultBuffer.append(mCodeBook!![index - 1])
            mHanlder!!.sendEmptyMessage(MSG_SET_RECG_TEXT)
        }
    }


    override fun onStopRecognition() {
        LogHelper.d(TAG, "stop recognition")
    }

    private class RegHandler(reco: SinVoiceRecognition) : Handler() {
        private var sinReco: WeakReference<SinVoiceRecognition> = WeakReference(reco)

        override fun handleMessage(msg: Message) {
            val mListener = sinReco.get()?.getListener()
            when (msg.what) {
                MSG_RECG_START -> mListener?.onRecognitionStart()
                MSG_RECG_END -> mListener?.onRecognitionEnd()
                MSG_SET_RECG_TEXT -> outputResult(mListener)
            }
        }

        private fun outputResult(mListener: Listener?) {
            val resultBuffer = sinReco.get()?.resultBuffer!!
            if (resultBuffer.length >= 4) {
                val res = StringBuilder()
                for (s in 0..(resultBuffer.length - 4) step 4) {
                    val chr = Common.CHAR_MAP[resultBuffer.subSequence(s, s + 4)]
                    if (chr != null) {
                        res.append(chr)
                    }
                }
                mListener?.onRecognition(res.toString())
            }
        }
    }

    companion object {
        private val TAG = "SinVoiceRecognition"

        private val STATE_START = 1
        private val STATE_STOP = 2
        private val STATE_PENDING = 3

        // 识别成功
        private val MSG_SET_RECG_TEXT = 1
        // 开始识别
        private val MSG_RECG_START = 2
        // 识别结束
        private val MSG_RECG_END = 3
    }

}